#include "cgeDatagramSocket.h"


namespace basterd
{
	namespace net
	{
	    // ----------------------------------------------------------------------------
		// DatagramSocket
		// ----------------------------------------------------------------------------
        DatagramSocket::DatagramSocket( unsigned short porta, bool nonBlocking)
            : address(), socketId(0), hostAddress("127.0.0.1"), port(porta)
        {
        	// TODO inicializar API socket do windows

            create();
            bind(porta);

			if( nonBlocking )
			{
				setNonBlocking(true);
			}
        }
        // ----------------------------------------------------------------------------
        DatagramSocket::DatagramSocket( const String& hosta, unsigned short porta, bool nonBlocking  )    
            : address(), socketId(0), hostAddress(hosta), port(porta)
        {
			// TODO inicializar API do windows

            create();
            bind(hostAddress, porta);

			if( nonBlocking )
			{
				setNonBlocking(true);
			}
        }
        // ----------------------------------------------------------------------------
        DatagramSocket::~DatagramSocket(void)
        {
			close();
        }
        // ----------------------------------------------------------------------------
        bool DatagramSocket::isOpen(void) const
		{
			return socketId > 0;
		}
		// ----------------------------------------------------------------------------
		unsigned short DatagramSocket::getPort(void) const
		{
			return port;
		}
		// ----------------------------------------------------------------------------
		const String& DatagramSocket::getHostAddress(void) const
		{
			return hostAddress;
		}
		// ----------------------------------------------------------------------------
		bool DatagramSocket::setNonBlocking(bool noblock)
		{
#if (CGE_PLATFORM == CGE_APPLE) || (CGE_PLATFORM == CGE_LINUX) || (CGE_PLATFORM == CGE_LIN64)

			int nonBlocking = noblock ? 1 : 0;
			if ( fcntl( socketId, F_SETFL, O_NONBLOCK, nonBlocking ) == -1 )
			{
				if( noblock )
				{
					CGE_ERR( "Failed to set non-blocking socket" );
				}else
				{
					CGE_ERR( "Failed to set blocking socket" );
				}

				return false;
			}
#elif CGE_PLATFORM == CGE_WIN32

			DWORD nonBlocking = noblock ? 1 : 0;
			if ( ioctlsocket( socketId, FIONBIO, &nonBlocking ) != 0 )
			{
				if( noblock )
				{
					CGE_ERR( "Failed to set non-blocking socket" );
				}else
				{
					CGE_ERR( "Failed to set blocking socket" );
				}

				return false;
			}
#endif

			return true;
		}
		// ----------------------------------------------------------------------------
		bool DatagramSocket::create(void)
		{
		#if CGE_PLATFORM == CGE_WIN32
			memset( &address, 0, sizeof(__socketAddress) );

			socketId = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

			if ( socketId <= 0 )
			{
				CGE_ERRF("Cannot create datagram socket");
				return false;
			}

			return true;

		#elif (CGE_PLATFORM == CGE_APPLE) || (CGE_PLATFORM == CGE_LINUX) || (CGE_PLATFORM == CGE_LIN64)
			// ----------------------------------------------------
			// linux implementation
			memset( &address, 0, sizeof(__socketAddress) );

			socketId = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

			if ( socketId <= 0 ) 
			{
				// ERROR	
				CGE_ERRF("Cannot create datagram socket");
				return false;	
			}
			
			return true;

		#endif
		}
		// ----------------------------------------------------------------------------
		bool DatagramSocket::bind(unsigned short port)
		{
			this->port = port;

			// cross-platform implementation
			address.sin_family = AF_INET;
            address.sin_addr.s_addr = INADDR_ANY;
            address.sin_port = htons(port);

            int bind_return = ::bind( socketId, (struct sockaddr*) &address, sizeof(address) );

            if (bind_return == -1)
            {
				CGE_ERRF("Cannot bind socket to port " << port);
                return false;
            }

			return true;
		}
		// ----------------------------------------------------------------------------
		bool DatagramSocket::bind(const String& host, mushort port)
		{
			this->hostAddress = host;
			this->port = port;

            // ----------------------------------------------------
			// linux implementation
			address.sin_family = AF_INET;
            address.sin_addr.s_addr = INADDR_ANY;
            address.sin_port = htons(port);

            int bind_return = ::bind( socketId, (struct sockaddr*) &address, sizeof(address) );

            if (bind_return == -1)
            {
				CGE_ERRF("Cannot bind datagram socket at " << host << ":" << port);
                return false;
            }

			return true;
		}
		// ----------------------------------------------------------------------------
		int DatagramSocket::receive(DatagramPacket& packet)
		{
		#ifdef _WIN32
			// ----------------------------------------------------
			// windows implementation
			int sizeAddressPacket = sizeof(packet.address);

			int flag = 0;

			int status = ::recvfrom( socketId, packet.buffer, packet.length, flag,
			    (sockaddr*) &packet.address, (socklen_t*) &sizeAddressPacket );

			return status;            
            
        #else
            // ----------------------------------------------------
			// linux implementation
			int sizeAddressPacket = sizeof(packet.address);
			
			int flag = 0;
			
            int status = ::recvfrom( socketId, (void*) packet.buffer, packet.length, flag,
                (sockaddr*) &packet.address, (socklen_t*) &sizeAddressPacket );
                
            return status;
            
        #endif
		}
		// ----------------------------------------------------------------------------
		int DatagramSocket::send(const DatagramPacket& packet)
		{
		#ifdef _WIN32
            // ----------------------------------------------------
			// windows implementation            
			return ::sendto( socketId, packet.buffer, packet.length, 0,
				(sockaddr*) &packet.address, sizeof(sockaddr_in) );
            
        #elif (CGE_PLATFORM == CGE_APPLE) || (CGE_PLATFORM == CGE_LINUX) || (CGE_PLATFORM == CGE_LIN64)
            // ----------------------------------------------------
			// linux implementation			
			return ::sendto( socketId, packet.buffer, packet.length, 0,
			    (sockaddr*) &packet.address, sizeof(sockaddr_in) );
        
        #endif
        }
        // ----------------------------------------------------------------------------
		void DatagramSocket::close(void)
		{
		#if (CGE_PLATFORM == CGE_APPLE) || (CGE_PLATFORM == CGE_LINUX) || (CGE_PLATFORM == CGE_LIN64)
			::close( socketId );
		#elif CGE_PLATFORM == CGE_WIN32
			::closesocket( socketId );
		#endif

			socketId = -1;
		}
		// ----------------------------------------------------------------------------
        
	}
}
